// 定义策略接口
interface ValidationStrategy {
  option?: RuleOption
  validate(value: any): string | undefined
}

export interface RuleOption {
  message?: string
  fieldName?: string
  validate?: (value?: any) => string | undefined
}

export type FieldRule<T> = {
  [key in keyof T]?: ValidationStrategy[] | ValidationStrategy
}

// 定义策略类
export class NonEmptyStrategy implements ValidationStrategy {
  option: RuleOption

  constructor(option: RuleOption) {
    this.option = option
  }

  validate(value: string) {
    if (!value)
      return this.option.message ? this.option.message : `${this.option.fieldName ?? ''}不能为空`
  }
}

export class MobileStrategy implements ValidationStrategy {
  option?: RuleOption | undefined

  constructor(option?: RuleOption) {
    this.option = option
  }

  validate(value: string) {
    if (!value) return

    if (!/^1[3456789]\d{9}$/.test(value))
      return this.option?.message ? this.option.message : '请输入正确的手机号码'
  }
}

export class CustomStrategy implements ValidationStrategy {
  option?: RuleOption | undefined

  constructor(option?: RuleOption) {
    this.option = option
  }

  validate(): string | undefined {
    return this.option?.validate?.()
  }
}

/**
 * 使用策略模式校验表单数据。
 * 使用示例

```js
const formData = {
  username: '',
  password: '',
  mobile: '13912345678',
  email: 'admin@example.com',
}

const rules: FieldRule<typeof formData> = {
  username: [new NonEmptyStrategy({ fieldName: '用户名' })],
  password: new NonEmptyStrategy({ fieldName: '密码' }),
}

console.log(FormValidator.validate(formData, rules)) // 输出：用户名不能为空

```
 *
 */

export class FormValidator {
  static validate<T extends Record<string, any>>(formData: T, rules: FieldRule<T>) {
    // 使用`Object.entries()`替换`for..in`来迭代对象, 这种方式更安全、高效
    for (const [field, strategies] of Object.entries(rules)) {
      const value = formData[field]

      if (!strategies) return undefined

      // 使用`Array.isArray()`方法来检查 strategies 是对象还是对象数组，这种方式表达的意思更加明确。

      // 使用`map()`和`filter()`为每个策略生成错误消息数组，然后过滤掉任何虚假值。 这是一种更简洁、更实用的处理数组的方法。
      const messages = Array.isArray(strategies)
        ? strategies.map((strategy) => strategy.validate(value)).filter(Boolean)
        : [strategies.validate(value)].filter(Boolean)

      // 使用 `if (messages.length > 0)` 代替 `if (message)` 来处理有多个错误消息的情况。
      if (messages.length > 0) return messages[0]
    }
  }
}

export function validatePhoneNumber(phoneNumber: string): boolean {
  const regExp = /^1[3456789]\d{9}$/
  return regExp.test(phoneNumber)
}
